//
// RS Game Framework
// Copyright © 2010 Jedd Haberstro
// jhaberstro@gmail.com
// 
// $Id:
//

#ifndef RS_REFLECTION_CLASS_IPP
#define RS_REFLECTION_CLASS_IPP

#ifndef RS_REFLECTION_CLASS_IMPLEMENTATION_GUARD
#   error "Do not include this file directly. Instead, include rs/reflection/Class.hpp"
#endif

namespace rs
{
    namespace reflection
    {
        inline Boolean operator==(Class const& lhs, Class const& rhs) {
            return lhs.Equals(rhs);
        }
        
        inline Boolean operator!=(Class const& lhs, Class const& rhs) {
            return !lhs.Equals(rhs);
        }
                
        inline Class::Class(hidden::ClassBuilderInterface const* builder)
        : destructor_(builder->GetDestructor()),
          name_(builder->GetClassName()),
          parent_(builder->GetParentClass()),
          attributes_(builder->BuildAttributes()),
          methods_(builder->BuildMethods()),
          attributeCount_(builder->GetAttributeCount()),
          methodCount_(builder->GetMethodCount()) {
              const_cast< hidden::ClassBuilderInterface* >(builder)->FinalizeClassVisitor();
              for (std::vector< Constructor >::const_iterator itr = builder->GetConstructors().begin(); itr != builder->GetConstructors().end(); ++itr) {
                  constructors_[itr->GetArgumentCount()].push_back(*itr);
              }
              
              ReflectionCenter::GetReflectionCenter().RegisterClass(this);
        }
        
        template< typename TClassBuilder >
        inline Class::Class(TClassBuilder const& builder)
        : destructor_(builder.GetDestructor()),
          name_(builder.GetClassName()),
          parent_(builder.GetParentClass()),
          attributes_(builder.BuildAttributes()),
          methods_(builder.BuildMethods()),
          attributeCount_(builder.GetAttributeCount()),
          methodCount_(builder.GetMethodCount()) {
          TClassBuilder const* pbuilder = &builder;
              const_cast< TClassBuilder* >(pbuilder)->FinalizeClassVisitor();
              for (std::vector< Constructor >::const_iterator itr = builder.GetConstructors().begin(); itr != builder.GetConstructors().end(); ++itr) {
                  constructors_[itr->GetArgumentCount()].push_back(*itr);
              }

              ReflectionCenter::GetReflectionCenter().RegisterClass(this);
        }
        
        inline Class::~Class() {
            delete [] attributes_;
            attributes_ = 0;
            delete [] methods_;
            methods_ = 0;
        }
        
        template< typename TClass, typename TAttributeType >
        inline void Class::SetAttributesValue(TClass& instance, Attribute const* attribute, TAttributeType value) {
            RS_ASSERT(attribute != 0, "attribute is null");
            RS_ASSERT(TClass::ClassRecord.GetAttributeViaName(attribute->GetName()) != 0, "attribute does not belong to class");
            RS_ASSERT(ConvertToQualifiedType< TAttributeType >::kQualifiedType == attribute->GetType().ToIntegral(), "Attribute's type and template type do not match");
            *((reinterpret_cast< TAttributeType* >(reinterpret_cast< UIntPtr >(&instance) + attribute->GetOffset()))) = value;            
        }
        
        template< typename TClass, typename TAttributeType >
        inline void Class::SetAttributesValue(TClass& instance, UInt32 index, TAttributeType value) {
            Attribute const* attribute = TClass::ClassRecord.GetAttribute(index);
            RS_ASSERT(attribute != 0, "attribute is null");
            RS_ASSERT(TClass::ClassRecord.GetAttributeViaName(attribute->GetName()) != 0, "attribute does not belong to class");
            RS_ASSERT(ConvertToQualifiedType< TAttributeType >::kQualifiedType == attribute->GetType().ToIntegral(), "Attribute's type and template type do not match");
            *((reinterpret_cast< TAttributeType* >(reinterpret_cast< UIntPtr >(&instance) + attribute->GetOffset()))) = value;
        }
        
        inline void Class::SetAttributesValue(void* instance, Attribute const* attribute, void const* value, RsSize size) {
            RS_ASSERT(instance != 0, "instance is null");
            RS_ASSERT(attribute != 0, "attribute is null");
            RS_ASSERT(value != 0, "value is null");
            std::memcpy(static_cast< Int8* >(instance) + attribute->GetOffset(), value, size);
        }
        
        template< typename TClass, typename TAttributeType >
        inline void Class::GetAttributesValue(TClass& instance, Attribute const* attribute, TAttributeType& value) {
            RS_ASSERT(attribute != 0, "attribute is null");
            RS_ASSERT(TClass::ClassRecord.GetAttributeViaName(attribute->GetName()) != 0, "attribute does not belong to class");
            RS_ASSERT(ConvertToQualifiedType< TAttributeType >::kQualifiedType == attribute->GetType().ToIntegral(), "Attribute's type and template type do not match");
            value = *reinterpret_cast< TAttributeType* >(reinterpret_cast< UIntPtr >(&instance) + attribute->GetOffset());
        }
        
        template< typename TClass, typename TAttributeType >
        inline void Class::GetAttributesValue(TClass& instance, UInt32 index, TAttributeType& value) {
            Attribute const* attribute = TClass::ClassRecord.GetAttribute(index);
            RS_ASSERT(attribute != 0, "attribute is null");
            RS_ASSERT(TClass::ClassRecord.GetAttributeViaName(attribute->GetName()) != 0, "attribute does not belong to class");
            RS_ASSERT(ConvertToQualifiedType< TAttributeType >::kQualifiedType == attribute->GetType().ToIntegral(), "Attribute's type and template type do not match");
            value = *reinterpret_cast< TAttributeType* >(reinterpret_cast< UIntPtr >(&instance) + attribute->GetOffset());
        }
        
        inline void Class::GetAttributesValue(void* instance, Attribute const* attribute, char* value, RsSize size) {
            // ADD A SIZE FIELD TO Attribute AND CHECK IT HERE FOR SAFETY?
            RS_ASSERT(attribute != 0, "attribute is null");
            char const* ptr = static_cast< char* >(instance) + attribute->GetOffset();
            std::memcpy(value, ptr, size);
        }
        
        inline void* Class::Construct() const { 
            RS_ASSERT(constructors_.find(0) != constructors_.end(), "constructors_ does not contains an entry for desired argument count 0");
            std::vector< Constructor > const& constructors = constructors_.at(0);
            RS_ASSERT(constructors.size() != 0, "No matching constructors");
            RS_ASSERT(constructors.size() == 1, "To many constructors with no arguments");
            Constructor const& constructor = constructors.at(0); 
            return constructor();
        }
        
        template< typename TArg > 
        void* Class::Construct(TArg arg) const { 
            RS_ASSERTF(constructors_.find(0+1) != constructors_.end(), "constructors_ does not contains an entry for desired argument count %i", 0+1); 
            std::vector< Constructor > const& constructors = constructors_.at(0+1); 
            Constructor result;
            UInt64 type = ConvertToQualifiedType< TArg >::kQualifiedType;
            if (ConstructImpl(constructors, &type, 1, result)) {
                return result(arg);
            }
            
            RS_ASSERT(false, "Failed to find matching constructor"); 
            return 0; 
        }
        
        template< typename TArg, typename TArg1 > 
        void* Class::Construct(TArg arg, TArg1 arg1) const { 
            RS_ASSERTF(constructors_.find(2) != constructors_.end(), "constructors_ does not contains an entry for desired argument count %i", 2); 
            std::vector< Constructor > const& constructors = constructors_.at(2); 
            Constructor result;
            UInt64 types[2] = { ConvertToQualifiedType< TArg >::kQualifiedType, ConvertToQualifiedType< TArg1 >::kQualifiedType };
            if (ConstructImpl(constructors, types, 2, result)) {
                return result(arg, arg1);
            }
            
            RS_ASSERT(false, "Failed to find matching constructor"); 
            return 0; 
        }
        
        template< typename TArg, typename TArg1, typename TArg2 > 
        void* Class::Construct(TArg arg, TArg1 arg1, TArg2 arg2) const { 
            RS_ASSERTF(constructors_.find(3) != constructors_.end(), "constructors_ does not contains an entry for desired argument count %i", 3); 
            std::vector< Constructor > const& constructors = constructors_.at(3); 
            Constructor result;
            UInt64 types[3] = {
                ConvertToQualifiedType< TArg >::kQualifiedType,
                ConvertToQualifiedType< TArg1 >::kQualifiedType,
                ConvertToQualifiedType< TArg2 >::kQualifiedType
            };
            
            if (ConstructImpl(constructors, types, 3, result)) {
                return result(arg, arg1, arg2);
            }
            
            RS_ASSERT(false, "Failed to find matching constructor"); 
            return 0;
        }
        
        template< typename TArg, typename TArg1, typename TArg2, typename TArg3 > 
        void* Class::Construct(TArg arg, TArg1 arg1, TArg2 arg2, TArg3 arg3) const { 
            RS_ASSERTF(constructors_.find(4) != constructors_.end(), "constructors_ does not contains an entry for desired argument count %i", 4); 
            std::vector< Constructor > const& constructors = constructors_.at(4); 
            Constructor result;
            UInt64 types[4] = {
                ConvertToQualifiedType< TArg >::kQualifiedType,
                ConvertToQualifiedType< TArg1 >::kQualifiedType,
                ConvertToQualifiedType< TArg2 >::kQualifiedType,
                ConvertToQualifiedType< TArg3 >::kQualifiedType
            };
            
            if (ConstructImpl(constructors, types, 4, result)) {
                return result(arg, arg1, arg2, arg3);
            }
            
            RS_ASSERT(false, "Failed to find matching constructor"); 
            return 0; 
        }
        
        template< typename TArg, typename TArg1, typename TArg2, typename TArg3, typename TArg4 > 
        void* Class::Construct(TArg arg, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4) const { 
            RS_ASSERTF(constructors_.find(5) != constructors_.end(), "constructors_ does not contains an entry for desired argument count %i", 4); 
            std::vector< Constructor > const& constructors = constructors_.at(5); 
            Constructor result;
            UInt64 types[5] = {
                ConvertToQualifiedType< TArg >::kQualifiedType,
                ConvertToQualifiedType< TArg1 >::kQualifiedType,
                ConvertToQualifiedType< TArg2 >::kQualifiedType,
                ConvertToQualifiedType< TArg3 >::kQualifiedType,
                ConvertToQualifiedType< TArg4 >::kQualifiedType
            };
            
            if (ConstructImpl(constructors, types, 5, result)) {
                return result(arg, arg1, arg2, arg3, arg4);
            }
            
            RS_ASSERT(false, "Failed to find matching constructor"); 
            return 0; 
        }
        
        inline void Class::Construct(void* dest) const {
            RS_ASSERT(constructors_.find(0) != constructors_.end(), "constructors_ does not contains an entry for desired argument count 0");
            std::vector< Constructor > const& constructors = constructors_.at(0);
            RS_ASSERT(constructors.size() != 0, "No matching constructors");
            RS_ASSERT(constructors.size() == 1, "To many constructors with no arguments");
            Constructor const& constructor = constructors.at(0); 
            constructor(dest);
        }
        
        template< typename TArg > 
        void Class::Construct(void* dest, TArg arg) const {
            RS_ASSERTF(constructors_.find(0+1) != constructors_.end(), "constructors_ does not contains an entry for desired argument count %i", 0+1); 
            std::vector< Constructor > const& constructors = constructors_.at(0+1); 
            Constructor result;
            UInt64 types = ConvertToQualifiedType< TArg >::kQualifiedType;
            
            if (ConstructImpl(constructors, &types, 1, result)) {
                result(dest, arg);
                return;
            }
            
            RS_ASSERT(false, "Failed to find matching constructor"); 
        }
        
        template< typename TArg, typename TArg1 > 
        void Class::Construct(void* dest, TArg arg, TArg1 arg1) const {
            RS_ASSERTF(constructors_.find(2) != constructors_.end(), "constructors_ does not contains an entry for desired argument count %i", 2); 
            std::vector< Constructor > const& constructors = constructors_.at(2); 
            Constructor result;
            UInt64 types[] = {
                ConvertToQualifiedType< TArg >::kQualifiedType,
                ConvertToQualifiedType< TArg1 >::kQualifiedType
            };
            
            if (ConstructImpl(constructors, types, 2, result)) {
                result(dest, arg, arg1);
                return;
            }
            
            RS_ASSERT(false, "Failed to find matching constructor"); 
        }
        
        template< typename TArg, typename TArg1, typename TArg2 > 
        void Class::Construct(void* dest, TArg arg, TArg1 arg1, TArg2 arg2) const {
            RS_ASSERTF(constructors_.find(3) != constructors_.end(), "constructors_ does not contains an entry for desired argument count %i", 3); 
            std::vector< Constructor > const& constructors = constructors_.at(3); 
            Constructor result;
            UInt64 types[] = {
                ConvertToQualifiedType< TArg >::kQualifiedType,
                ConvertToQualifiedType< TArg1 >::kQualifiedType,
                ConvertToQualifiedType< TArg2 >::kQualifiedType
            };
            
            if (ConstructImpl(constructors, types, 3, result)) {
                result(dest, arg, arg1, arg2);
                return;
            }
            
            RS_ASSERT(false, "Failed to find matching constructor"); 
        }
        
        template< typename TArg, typename TArg1, typename TArg2, typename TArg3 > 
        void Class::Construct(void* dest, TArg arg, TArg1 arg1, TArg2 arg2, TArg3 arg3) const {
            RS_ASSERTF(constructors_.find(4) != constructors_.end(), "constructors_ does not contains an entry for desired argument count %i", 4); 
            std::vector< Constructor > const& constructors = constructors_.at(4); 
            Constructor result;
            UInt64 types[] = {
                ConvertToQualifiedType< TArg >::kQualifiedType,
                ConvertToQualifiedType< TArg1 >::kQualifiedType,
                ConvertToQualifiedType< TArg2 >::kQualifiedType,
                ConvertToQualifiedType< TArg3 >::kQualifiedType
            };
            
            if (ConstructImpl(constructors, types, 4, result)) {
                result(dest, arg, arg1, arg2, arg3);
                return;
            }
            
            RS_ASSERT(false, "Failed to find matching constructor"); 
        }
        
        template< typename TArg, typename TArg1, typename TArg2, typename TArg3, typename TArg4 > 
        void Class::Construct(void* dest, TArg arg, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4) const {
            RS_ASSERTF(constructors_.find(5) != constructors_.end(), "constructors_ does not contains an entry for desired argument count %i", 4); 
            std::vector< Constructor > const& constructors = constructors_.at(5); 
            Constructor result;
            UInt64 types[] = {
                ConvertToQualifiedType< TArg >::kQualifiedType,
                ConvertToQualifiedType< TArg1 >::kQualifiedType,
                ConvertToQualifiedType< TArg2 >::kQualifiedType,
                ConvertToQualifiedType< TArg3 >::kQualifiedType,
                ConvertToQualifiedType< TArg4 >::kQualifiedType
            };
            
            if (ConstructImpl(constructors, types, 5, result)) {
                result(dest, arg, arg1, arg2, arg3, arg4);
                return;
            }
            
            RS_ASSERT(false, "Failed to find matching constructor"); 
        }
        
        inline void Class::Destruct(void* object) const {
            RS_ASSERT(object != 0, "'object' is null. Can't be destructed");
            destructor_(object);
        }
            
        inline Attribute const* Class::GetAttribute(UInt32 index) const {
            RS_ASSERT(index < attributeCount_, "index out of range");
            return &attributes_[index];
        }
        
        inline Method const& Class::GetMethod(UInt32 index) const {
            RS_ASSERT(index < methodCount_, "index out of range");
            return methods_[index];
        }
                
        inline UInt32 Class::GetAttributeCount() const {
            return attributeCount_;
        }
        
        inline UInt32 Class::GetMethodCount() const {
            return methodCount_;
        }
        
        inline Char8 const* Class::GetName() const {
            return name_;
        }
    }
}

#endif // RS_REFLECTION_CLASS_IPP
